package net.cyndeline.rldungeon.dgs.strategy.help

import net.cyndeline.rldungeon.dgs.graph.{Room}
import scalax.collection.GraphEdge.UnDiEdge
import scalax.collection.immutable.Graph
import net.cyndeline.rlgraph.pathfinding.djikstra.DjikstraShortestPath
import net.cyndeline.rlgraph.util.{EdgeCopyFactory, VertexReplacer}

/**
 * Used for injection.
 */
trait ShortestDistanceDesignatorI {
  def addShortestDistances[VType <: Room[VType], EType[X] <: UnDiEdge[X]](graph: Graph[VType, EType],
                                                                          goal: VType,
                                                                          edgeFactory: EdgeCopyFactory[VType, EType]): GraphModificationResult[VType, EType]
}
/**
 * Computes the shortest distance to every room in a level from a given room, and stores that information
 * in the level.
 */
class ShortestDistanceDesignator extends ShortestDistanceDesignatorI {

  /**
   *
   * @param graph A graph representing connecting level topology.
   * @param goal The exit of the level.
   * @param edgeFactory Factory creating new edges from pair of vertices.
   * @return A copy of the input graph with the shortest distance to goal set for every vertex.
   */
  def addShortestDistances[VType <: Room[VType], EType[X] <: UnDiEdge[X]](graph: Graph[VType, EType],
                                                                          goal: VType,
                                                                          edgeFactory: EdgeCopyFactory[VType, EType]): GraphModificationResult[VType, EType] = {
    if (!graph.isConnected)
      throw new IllegalArgumentException("Shortest distances can only be computed for connected graphs. The following graph was disconnected: " + graph)

    val djikstraAlg = new DjikstraShortestPath(graph)
    val vertexReplacer = new VertexReplacer[VType, EType](edgeFactory)
    val allRooms = graph.nodes.toOuter.toSet[VType].iterator
    var oldToNewmap = Map[VType, VType]()
    var updatedGraph = graph

    while (allRooms.hasNext) {
      val room = allRooms.next()
      val distanceToGoal = djikstraAlg.distance(goal, room)
      val newRoom = room.withDistanceToGoal(distanceToGoal)
      oldToNewmap += (room -> newRoom)
      updatedGraph = vertexReplacer.replace(room, newRoom, updatedGraph)
    }

    GraphModificationResult(updatedGraph, oldToNewmap)
  }
}
